function PDRender() {
    this.viewScale = 1;
    this.pdPage = null;
    this.domDoc = null;
    this.canvasBuf = null;
    this.canvas = null;
    this.matrix = null;
    this.viewRotate = null;
    this.m_curObj = null; //currently rendering object
    this.m_lastClip = null; // last clipping path points. a PDClipPathState instance
}

PDRender.prototype.AttachCanvas = function (cns) {
    this.canvas = cns;
    this.domDoc = cns.ownerDocument;
    this.canvasBuf = this.domDoc.createElement("canvas");
};

PDRender.prototype.AttachPage = function (page) {
    this.pdPage = page;
};

PDRender.prototype.Render = function (xPos, yPos, xSize, ySize, rotate) {
    if (null == this.canvas)
        return;
    this.matrix = this.pdPage.GetDisplayMatrix(xPos, yPos, xSize, ySize, rotate);
    this.viewRotate = rotate % 4;

    // reset the canvas's dimension
    if (this.canvasBuf.width != this.canvas.width || this.canvasBuf.height != this.canvas.height) {
        this.canvasBuf.width = this.canvas.width;
        this.canvasBuf.height = this.canvas.height;
    }

    var ctx = this.canvas.getContext("2d"); //this.canvasBuf.getContext("2d");
    ctx.save();
    ctx.clearRect(0, 0, this.canvasBuf.width, this.canvasBuf.height);
    ctx.fillStyle = "white"; //this.pdPage.bgColor;
    ctx.fillRect(0, 0, this.canvasBuf.width, this.canvasBuf.height);
    ctx.restore();
    this.RenderPageObjs();
    //this.canvas.getContext("2d").drawImage(this.canvasBuf, 0, 0);
};

PDRender.prototype.RenderSingleObj = function (obj, mtObj2Device) {
    m_curObj = obj;
    // process single now. First clipping
    this.ProcessClipPath(obj, mtObj2Device);

    //then process transparency
    //if(this.ProcessTransparency(obj)) return;

    this.ProcessSingleObjNoClip(obj, mtObj2Device);
};

PDRender.prototype.ProcessSingleObjNoClip = function (obj, mtObj2Device) {
    if (obj == undefined || obj == null) return;

    //   this.SetColorAndGraphState(obj.nClrStateID, obj.nGraphStateID);
    if (obj instanceof TextObj) {
        this.RenderText(obj, mtObj2Device);
    }
    else if (obj instanceof ImgObj) {
        this.RenderImg(obj, mtObj2Device);
    }
    else if (obj instanceof PathObj) {
        this.RenderPath(obj, mtObj2Device);
    }
    else if (obj instanceof ShadingObj) {
        this.RenderShading(obj, mtObj2Device);
    } else if (obj instanceof InlineImgObj) {
        this.RenderImg(obj, mtObj2Device);
    } else if (obj instanceof FormObj) {
        this.RenderObjList(obj.form.objArray);
    }
};
PDRender.prototype.SetColorAndGraphState = function (ctx, colorID, graphID) {
    if (-1 == colorID || -1 == graphID) return;
    //set colors
   // var ctx = this.canvas.getContext("2d");// this.canvasBuf.getContext("2d");
    var clr = this.GetColors(colorID);
    ctx.strokeStyle = clr[0];
    ctx.fillStyle = clr[1];

    //set graph properties
    var graphState = this.pdPage.GetGraphStateByID(graphID);

    // line cap style
    switch (graphState.nCapStyle) {
        case 0: // Butt cap
            ctx.lineCap = "butt";
            break;
        case 1: // Round cap
            ctx.lineCap = "round";
            break;
        case 2: // Square cap
            ctx.lineCap = "square";
            break;
    }
    // line join style
    switch (graphState.joinStyle) {
        case 0: // Miter join
            ctx.lineJoin = "miter";
            break;
        case 1: // Round join
            ctx.lineJoin = "round";
            break;
        case 2: // Bevel join
            ctx.lineJoin = "bevel";
            break;
    }
    // line width
    ctx.lineWidth = graphState.fLineWidth; // should consider matrix  pt-> px??
    var n = ctx.lineWidth;
    // line join miter limit
    ctx.miterLimit = graphState.fJoinMiterLimit;
};



PDRender.prototype.GetColors = function (colorID) {
    if (colorID < 0) return;
    var colorState = this.pdPage.GetClrStateByID(colorID);
    var b = (colorState.strokeRGB >> 16) & 0xFF;
    var g = (colorState.strokeRGB >> 8) & 0xFF;
    var r = colorState.strokeRGB & 0xFF;
    var strokeRGB = "RGB(" + r + "," + g + "," + b + ")";
    var b = (colorState.fillRGB >> 16) & 0xFF;
    var g = (colorState.fillRGB >> 8) & 0xFF;
    var r = colorState.fillRGB & 0xFF;
    var fillRGB = "RGB(" + r + "," + g + "," + b + ")";
    var rst = [];
    rst[0] = strokeRGB;
    rst[1] = fillRGB;
    return rst;
};

PDRender.prototype.ProcessClipPath = function (obj, mtObj2Device) {
    var clipPathState = this.pdPage.GetClipPathStateByID(obj.nClipPathStateID);
    if (clipPathState == undefined || clipPathState == null) return;
    var generalState = this.pdPage.GetGenStateByID(obj.nGenStateID);

    if (this.m_lastClip != null && this.m_lastClip.Equal(clipPathState)) return;

    var clipPathData = clipPathState.clipPath;
    //set clipping
    for (var i = 0; i < clipPathData.length; i++) {
        var path = clipPathData[i];
        this.SetSingleClip(path, mtObj2Device);
    }
    this.m_lastClip = clipPathState;
};

//set a single clip, pathData:[[x,y,flag], [x,y,flag],...]
PDRender.prototype.SetSingleClip = function (pathData, mtObj2Device) {
    if (pathData == undefined || pathData == null || pathData.length == 0) return;
    var ctx = this.canvas.getContext("2d");// this.canvasBuf.getContext("2d");
    ctx.setTransform(mtObj2Device.a, mtObj2Device.b, mtObj2Device.c, mtObj2Device.d, mtObj2Device.e, mtObj2Device.f);
    ctx.beginPath();
    this.DoPath(ctx, pathData);
    ctx.closePath();
    ctx.clip(); //clip it
};

PDRender.prototype.RenderPageObjs = function () {
    var pageObjs = this.pdPage.GetPageObjects();
    this.RenderObjList(pageObjs, this.matrix);
};

PDRender.prototype.RenderObjList = function (arrObj, mtObj2Device) {
    if (arrObj == undefined || arrObj == null) return;
    for (var i = 0, objCount = arrObj.length; i < objCount; i++) {
        var obj = arrObj[i];
        this.RenderSingleObj(obj, mtObj2Device);
    }
};

PDRender.prototype.RenderText = function (textObj, mtObj2Device) {
    var ctx = this.canvas.getContext("2d");// this.canvasBuf.getContext("2d");

    ctx.save();
    this.SetColorAndGraphState(ctx, textObj.nClrStateID, textObj.nGraphStateID);

    var textState = this.pdPage.GetTextStateByID(textObj.nTextStateID);
    var generalState = this.pdPage.GetGenStateByID(textObj.nGenStateID);
    var fillAlpha = generalState.fillAlpha;
    var strokeAlpha = generalState.strokeAlpha;

    ctx.font = this.GetCSSFontInfo(textState);
    ctx.textAlign = "left";
    ctx.textBaseline = "alphabetic";

    var mtFinal = PDMatrix.prototype.Concat2mt(textObj.matrix, mtObj2Device);
    var mtTemp = new PDMatrix();
    if (textState.fontSize < 0)
        mtTemp.Set(-1, 0, 0, 1, 0, 0);
    else
        mtTemp.Set(1, 0, 0, -1, 0, 0);
    mtFinal = PDMatrix.prototype.Concat2mt(mtTemp, mtFinal);
    ctx.setTransform(mtFinal.a, mtFinal.b, mtFinal.c, mtFinal.d, mtFinal.e, mtFinal.f);

    var content = textObj.GetContent();
    var pos = textObj.GetOriginPoint();

    // draw text
    switch (textState.nTextMode) {
        case 0: // fill text
            ctx.globalAlpha = fillAlpha;
            //ctx.fillText(content, 0, 0);
            PDRender.prototype.DrawText(ctx, content, textState.charSpace, textState.wordSpace, true);
            break;
        case 1: // stroke text
            ctx.globalAlpha = strokeAlpha;
            //ctx.strokeText(content, 0, 0);
            PDRender.prototype.DrawText(ctx, content, textState.charSpace, textState.wordSpace, false);
            break;
        case 2: // fill, then stroke text
            ctx.globalAlpha = fillAlpha;
            //ctx.fillText(content, 0, 0);
            PDRender.prototype.DrawText(ctx, content, textState.charSpace, textState.wordSpace, true);
            ctx.globalAlpha = strokeAlpha;
            //ctx.strokeText(content, 0, 0);
            PDRender.prototype.DrawText(ctx, content, textState.charSpace, textState.wordSpace, false);
            break;
        case 3: // neither fill nor stroke text (invisible)
            break;
        case 4: // fill text and add to for clipping (see above)
            break;
        case 5: // stroke text and add to path for clipping
            break;
        case 6: // fill, then stroke text and add to path for clipping
            break;
        case 7: // add text to path for clipping
            break;
    }
    ctx.restore();
};

PDRender.prototype.GetCSSFontInfo = function (txtState) {

    // set text state
    var fontInfo = this.pdPage.pdDoc.GetFontInfoByID(txtState.nFontID);
    var fontProperty = "";

    // font-size/line-height
    var fontsize = Math.abs(txtState.fontSize * this.viewScale) / Math.sqrt(this.matrix.a * this.matrix.a + this.matrix.b * this.matrix.b);
    fontInfo.fontSize = fontsize;
    // font-family
    var fxname = fontInfo.fontFace;
    var temp = fxname.split("_");
    var family = "";
    fontProperty += fontsize + "pt ";
    if (fontInfo.isStandard == 1 || temp[1] == "STD") {
        family = baseFonts[temp[2]];
    } else if (fontInfo.isEmbedded == 1 || temp[1] == "CJK") {
        family = fxname;
    } else {
        family = temp[1];
    }
    fontInfo.fontFamily = family;
    return fontProperty += "'" + family + "'"; ;
};

PDRender.prototype.DrawText = function (ctx, content, charspace, wordspace, bfill) {
    var curChar;
    var curCharWidth;
    var i, len;

    if (charspace != 0 || wordspace != 0) {
        for (i = 0, len = content.length; i < len; i++) {
            curChar = content.charAt(i);
            if (bfill)
                ctx.fillText(curChar, 0, 0);
            else
                ctx.strokeText(curChar, 0, 0);
            curCharWidth = ctx.measureText(curChar).width;
            if (curChar != ' ') {
                ctx.translate(curCharWidth+charspace, 0);
            }
            else {
                ctx.translate(curCharWidth+wordspace, 0);
            }
        }
    }
    else {
        ctx.fillText(content, 0, 0);
    }

};

PDRender.prototype.RenderImg = function (imgObj, mtObj2Device) {
    if (imgObj == undefined || imgObj == null) return;
    var generalState = this.pdPage.GetGenStateByID(imgObj.nGenStateID);
    var fillAlpha = generalState.fillAlpha;
    var mtFinal = PDMatrix.prototype.Concat2mt(imgObj.matrix, mtObj2Device);

    var ctx = this.canvas.getContext("2d");// this.canvasBuf.getContext("2d");
    ctx.save();
    ctx.globalAlpha = fillAlpha;
    ctx.setTransform(mtFinal.a, mtFinal.b, -mtFinal.c, -mtFinal.d, mtFinal.e, mtFinal.f);
    ctx.translate(0, -1);
    ctx.drawImage(imgObj.domImg, 0, 0, 1, 1);
    ctx.restore();
};

PDRender.prototype.RenderPath = function (pathObj, mtObj2Device) {
    if (pathObj == undefined || pathObj == null) return;

    var ctx = this.canvas.getContext("2d"); // this.canvasBuf.getContext("2d");
    ctx.save();
    this.SetColorAndGraphState(ctx, pathObj.nClrStateID, pathObj.nGraphStateID);
    var generalState = this.pdPage.GetGenStateByID(pathObj.nGenStateID);
    var fillAlpha = generalState.fillAlpha;
    var strokeAlpha = generalState.strokeAlpha;
    var rel = [1, true];
    //// first check whether pattern is used
    if ((pathObj.fillShadingPattern != null) || (pathObj.strokeShadingPattern != null) || (pathObj.strokeTillingPattern != null) || (pathObj.fillTillingPattern != null)) {
        var fillType = pathObj.fillType;
        var bStroke = pathObj.isStrokePath;

        rel = this.ProcessPathPattern(pathObj, mtObj2Device, fillType, bStroke);
        if (rel[0] == 0 && rel[1] == false) {
            ctx.restore();
            return; // all processed
        }
    }

    var temp = [];
    for (var i = 0, len = pathObj.CountPathPoints(); i < len; i++) {
        temp[i] = pathObj.GetPathPoint(i);
    }

    var mtFinal = PDMatrix.prototype.Concat2mt(pathObj.matrix, mtObj2Device);
    ctx.setTransform(mtFinal.a, mtFinal.b, mtFinal.c, mtFinal.d, mtFinal.e, mtFinal.f);
    ctx.beginPath();
    this.DoPath(ctx, temp);
    ctx.closePath();
    if (pathObj.isStrokePath && rel[1] == true) {// stroke or fill the path
        ctx.globalAlpha = strokeAlpha;
        ctx.stroke();
    }
    if (pathObj.fillType === 1 && rel[0] != 0) { // odd-even-fill // html5 canvas not support
        ctx.globalAlpha = fillAlpha;
        ctx.fill();
    }
    else if (pathObj.fillType === 2 && rel[0] != 0) { // nonzero-fill
        ctx.globalAlpha = fillAlpha;
        ctx.fill();
    }
    ctx.restore();
};

PDRender.prototype.ProcessPathPattern = function (pathObj, mtObj2Device, fillType, bStroke) {
    var shCnvs = this.domDoc.createElement("canvas");
    shCnvs.width = this.canvas.width;
    shCnvs.height = this.canvas.height;
    var shCtx = shCnvs.getContext("2d");
    var dstCtx = this.canvas.getContext("2d");
    var dstImg = dstCtx.getImageData(0, 0, this.canvas.width, this.canvas.height);
    shCtx.putImageData(dstImg, 0, 0);

    this.SetColorAndGraphState(shCtx, pathObj.nClrStateID, pathObj.nGraphStateID);
    var generalState = this.pdPage.GetGenStateByID(pathObj.nGenStateID);
    var fillAlpha = generalState.fillAlpha;
    var strokeAlpha = generalState.strokeAlpha;
    var type = fillType;
    var bS = bStroke;

    if (bStroke) {
        if (this.DrawPathWithPattern(shCnvs, pathObj, mtObj2Device, 0, true)) {
            //copy data from shCnvs
            var rcDeviceDest = [];
            if (pathObj.position.length == 4)
                rcDeviceDest = mtObj2Device.TransFormRect(pathObj.position[0], pathObj.position[1], pathObj.position[2], pathObj.position[3]);
            var shImg = shCtx.getImageData(rcDeviceDest[0], rcDeviceDest[1], rcDeviceDest[2] - rcDeviceDest[0], rcDeviceDest[3] - rcDeviceDest[1]);
            dstCtx.putImageData(shImg, rcDeviceDest[0], rcDeviceDest[1]);

            bS = false;
        }
        //bS = false;
    }
    if (fillType) {
        type = 0;
        this.DrawPathWithPattern(this.canvas, pathObj, mtObj2Device, fillType, false);
    }
    return [type, bS];
};

//pathData:[[x,y, flag],[x,y,flag],...]
PDRender.prototype.PathIsRect = function (pathData) {
    var nPoint = pathData.length;
    if (nPoint != 5 && nPoint != 4) return false;

    if (nPoint == 5 && (pathData[0][0] != pathData[4][0] ||
			pathData[0][1] != pathData[4][1]) ||
			pathData[1][0] == pathData[3][0] && pathData[1][1] == pathData[3][1])
        return false;

    // All rectangle lines can only change one coord
    if (pathData[0][0] != pathData[3][0] && pathData[0][1] != pathData[3][1])
        return false;

        for (var i = 1; i < 4; i ++) {
            if ((pathData[i][2] & 6) != 2) return false;
            if (pathData[i][0] != pathData[i - 1][0] && pathData[i][1] != pathData[i - 1][1])
		        return false;
        }
    // If only 4 points, we need to make sure the last point is closed
   // TESTDOC: Bug #6843 - K1.pdf (editing)
    return nPoint == 5 || (pathData[3][2] & 1);
};

PDRender.prototype.DrawPathWithPattern = function (shCnvs, pathObj, mtObj2Device, fillType, bStroke) {
    if (pathObj == undefined || pathObj == null) return false;

    var generalState = this.pdPage.GetGenStateByID(pathObj.nGenStateID);
    var fillAlpha = generalState.fillAlpha;
    var strokeAlpha = generalState.strokeAlpha;

    var pathData = [];
    var nlen = pathObj.CountPathPoints();
    for (var k = 0; k < nlen; k++) {
        pathData[k] = pathObj.GetPathPoint(k);
    }

    var shCtx = shCnvs.getContext("2d");

    //  var ctx = this.canvas.getContext("2d"); // this.canvasBuf.getContext("2d");
    var mtFinal = PDMatrix.prototype.Concat2mt(pathObj.matrix, mtObj2Device);
    var rcDeviceDest = [];
    if (pathObj.position.length == 4)
        rcDeviceDest = mtFinal.TransFormRect(pathObj.position[0], pathObj.position[1], pathObj.position[2], pathObj.position[3]);
    //ctx.setTransform(mtFinal.a, mtFinal.b, mtFinal.c, mtFinal.d, mtFinal.e, mtFinal.f);

    if (fillType) {
        //we should set path as clipping area;
        this.SetSingleClip(pathData, mtFinal);

        if (pathObj.fillTillingPattern != undefined && pathObj.fillTillingPattern != null) {//tilling patter
            //DrawTilingPattern
            this.DrawTillingPattern(shCnvs, pathObj.fillTillingPattern, pathObj, mtObj2Device, bStroke);
        } else if (pathObj.fillShadingPattern != undefined && pathObj.fillShadingPattern != null) {
            this.DrawShadingPattern(shCnvs, pathObj.fillShadingPattern, mtObj2Device, rcDeviceDest, fillAlpha);
            return true;
        }
    }
    if (bStroke) {
        if (pathObj.strokeTillingPattern != undefined && pathObj.strokeTillingPattern != null) {//tilling patter
            //DrawTilingPattern
            this.DrawTillingPattern(shCnvs, pathObj.strokeTillingPattern, pathObj, mtObj2Device, bStroke);
        } else if (pathObj.strokeShadingPattern != undefined && pathObj.strokeShadingPattern != null) {
            this.DrawShadingPattern(shCnvs, pathObj.strokeShadingPattern, mtObj2Device, rcDeviceDest, fillAlpha);
        }

        //now we stroke path and composite it with shading as before
        shCtx.save();
        shCtx.globalCompositeOperation = "destination-in";
        shCtx.setTransform(mtFinal.a, mtFinal.b, mtFinal.c, mtFinal.d, mtFinal.e, mtFinal.f);

        var arrPath = [];
        var subPath = null;
        var len = pathData.length;
        for (var i = 0; i < len; i++) {
            var curPoint = pathData[i];
            var flag = curPoint[2];
            if (flag == 6) {
                if (subPath == null) {
                    subPath = new Array();
                } else {
                    arrPath.push(subPath);
                    subPath = null;
                    subPath = new Array();
                }
            }
            subPath.push(curPoint);

            if (i == len - 1) {
                arrPath.push(subPath);
            }
        }
        shCtx.beginPath();
        for (var j = 0; j < arrPath.length; j++) {
            var aPath = arrPath[j];

            // First a shortcut for rectangle filling
            // We take 4 or 5 points for a rectangle now, because some PDFs are too lazy to close the rectangle
            // TESTDOC: Bug #2316 - 5-1-1-1.pdf
            if (this.PathIsRect(aPath)) {
                shCtx.rect(aPath[0][0], aPath[0][1], aPath[2][0] - aPath[0][0], aPath[2][1] - aPath[0][1]);
                continue;
            } else {
                this.DoPath(shCtx, aPath);
            }
        }
        // ctx.closePath();//do not close the path
        shCtx.strokeStyle = "green"; //use a random color to stroke it
        shCtx.stroke();
        shCtx.restore();

        return true;
       // return false;
    }
    return false;
};

PDRender.prototype.DrawShadingPattern = function (shCnvs, shPattern, mtObj2Device, rcDeviceDest, fillAlpha) {
    var m = new PDMatrix();
    m.a = shPattern.m_mtPattern2Form[0];
    m.b = shPattern.m_mtPattern2Form[1];
    m.c = shPattern.m_mtPattern2Form[2];
    m.d = shPattern.m_mtPattern2Form[3];
    m.e = shPattern.m_mtPattern2Form[4];
    m.f = shPattern.m_mtPattern2Form[5];
    var mtFinal = PDMatrix.prototype.Concat2mt(m, mtObj2Device);

  //  shCtx.fillStyle = "red";
  //  shCtx.fillRect(0, 0, this.canvas.width, this.canvas.height)
       _DrawShadingPattern(shPattern, mtFinal, rcDeviceDest, fillAlpha, shCnvs/*this.canvasBuf*/);
   };

PDRender.prototype.DrawTillingPattern = function (tCnvs, tP, pathObj, mtObj2Device, rcDeviceDest, bStroke) {
       _DrawTillingPattern(tCnvs, tP, pathObj, mtObj2Device, rcDeviceDest, bStroke);
};

//pathData:in format [[x,y,flag], ...]
PDRender.prototype.DoPath = function (ctx, pathData) {
    var len = pathData.length;
    var bezierbeginIndex = -1, bezierendIndex = -1;
    //  ctx.beginPath();
    for (var i = 0; i < len; i++) {
        var curPoint = pathData[i];
        var flag = curPoint[2];
        switch (flag) {
            case 1:
                ctx.lineTo(pathData[0][0], pathData[0][1]);
                break;
            case 2:
            case 3:
                ctx.lineTo(curPoint[0], curPoint[1]);
                break;
            case 4:
            case 5:
                {
                    if (bezierbeginIndex === -1) {
                        bezierbeginIndex = i;
                    }
                    bezierendIndex = i;
                    if (i + 1 < len) {
                        if ((bezierendIndex - bezierbeginIndex + 1) % 3 === 0) {// outline the bezier curve
                            ctx.bezierCurveTo(pathData[bezierbeginIndex][0], pathData[bezierbeginIndex][1],
										pathData[bezierbeginIndex + 1][0], pathData[bezierbeginIndex + 1][1],
										pathData[bezierbeginIndex + 2][0], pathData[bezierbeginIndex + 2][1]);
                            bezierbeginIndex = -1;
                            bezierendIndex = -1;
                        }
                    }
                    else { // end the figure	// outline the bezier curve
                        var bezierPtCount = bezierendIndex - bezierbeginIndex + 1;
                        if (bezierPtCount % 3 === 0) {
                            ctx.bezierCurveTo(pathData[bezierbeginIndex][0], pathData[bezierbeginIndex][1],
										pathData[bezierbeginIndex + 1][0], pathData[bezierbeginIndex + 1][1],
										pathData[bezierbeginIndex + 2][0], pathData[bezierbeginIndex + 2][1]);
                            bezierbeginIndex = -1;
                            bezierendIndex = -1;
                        }
                    }
                }
                break;
            case 6:
                ctx.moveTo(curPoint[0], curPoint[1]);
                break;
            default:
                break;
        }
    }
    //  ctx.closePath();//if close path, canvase will close path automatickly, so it is wrong;
};

PDRender.prototype.RenderShading = function (shadingObj, mtObj2Device) {
    var ctx = this.canvas.getContext("2d");// this.canvasBuf.getContext("2d");

    var mtFinal = PDMatrix.prototype.Concat2mt(shadingObj.matrix, mtObj2Device);
    ctx.setTransform(mtFinal.a, mtFinal.b, mtFinal.c, mtFinal.d, mtFinal.e, mtFinal.f);
    //first we should set bbox as clipping area;
    if (shadingObj.BBox != null && shadingObj.BBox == 4) {
        ctx.beginPath();
        ctx.rect(shadingObj.BBox[0], shadingObj.BBox[1], shadingObj.BBox[2], shadingObj.BBox[3]);
        ctx.stroke();
        ctx.closePath();
        ctx.clip();
    }
    ctx.save();
    this.SetColorAndGraphState(ctx, shadingObj.nClrStateID, shadingObj.nGraphStateID);
    var generalState = this.pdPage.GetGenStateByID(pathObj.nGenStateID);
    var fillAlpha = generalState.fillAlpha;
    var strokeAlpha = generalState.strokeAlpha;
    var rcBBox = mtFinal.TransFormRect(shadingObj.BBox[0], shadingObj.BBox[1], shadingObj.BBox[2], shadingObj.BBox[3]);
    _DrawShadingPattern(shadingObj.shadingpattern, this.matrix, rcBBox, fillAlpha, this.canvasBuf);
    ctx.restore();
};
